<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf8">
    <!--

    @license twgl.js Copyright (c) 2015, Gregg Tavares All Rights Reserved.
    Available via the MIT license.
    see: http://github.com/greggman/twgl.js for details

    -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
    <meta property="og:title" content="TWGL.js - webgl2 textures" />
    <meta property="og:type" content="website" />
    <meta property="og:image" content="http://twgljs.org/examples/screenshots/webgl2-textures.png" />
    <meta property="og:description" content="TWGL.js - webgl2 textures" />
    <meta property="og:url" content="http://twgljs.org" />

    <meta name="twitter:card" content="summary_large_image">
    <meta name="twitter:site" content="@greggman">
    <meta name="twitter:creator" content="@greggman">
    <meta name="twitter:domain" content="twgljs.org">
    <meta name="twitter:title" content="TWGL.js - webgl2 textures">
    <meta name="twitter:url" content="http://twgljs.org/examples/webgl2-textures.html">
    <meta name="twitter:description" content="TWGL.js - webgl2 textures">
    <meta name="twitter:image:src" content="http://twgljs.org/examples/screenshots/webgl2-textures.png">

    <link href="/resources/images/twgljs-icon.png" rel="shortcut icon" type="image/png">

    <title>twgl.js - webgl2 textures</title>
    <style>
      body {
          margin: 0;
          font-family: monospace;
      }
      canvas {
          display: block;
          width: 100vw;
          height: 100vh;
      }
      #b {
        position: absolute;
        top: 10px;
        width: 100%;
        text-align: center;
        z-index: 2;
      }
      #no-webgl2 {
        position: absolute;
        left: 0;
        top: 0;
        background: red;
        color: white;
        width: 100vw;
        height: 100vh;
        display: flex;
        justify-content: center;
        align-items: center;
      }
    </style>
  </head>
  <body>
    <canvas id="c"></canvas>
    <div id="b"><a href="http://twgljs.org">twgl.js</a> - webgl2 textures</div>
    <div id="no-webgl2" style="display: none;">
      <div>Sorry but your browser doesn't appear to support WebGL2</div>
    </div>
  </body>
  <script id="one-point-vs" type="notjs">
#version 300 es
uniform mat4 u_worldViewProjection;
uniform mat4 u_world;

in vec4 a_position;
in vec2 a_texcoord;

out vec4 v_position;
out vec2 v_texCoord;

void main() {
  v_texCoord = a_texcoord;
  gl_Position = u_worldViewProjection * a_position;
}
  </script>
  <script id="one-point-fs" type="notjs">
#version 300 es
precision mediump float;

in vec4 v_position;
in vec2 v_texCoord;

uniform vec4 u_diffuseMult;
uniform sampler2D u_diffuse;

out vec4 outColor;

void main() {
  vec4 diffuseColor = texture(u_diffuse, v_texCoord) * u_diffuseMult;
  if (diffuseColor.a < 0.1) {
    discard;
  }
  outColor = diffuseColor;
  outColor.rbg *= outColor.a;
}
  </script>
  <script id="int-one-point-fs" type="notjs">
#version 300 es
precision mediump float;

in vec4 v_position;
in vec2 v_texCoord;

uniform ivec4 u_diffuseOffset;
uniform ivec4 u_diffuseMult;
uniform mediump isampler2D u_diffuse;

out vec4 outColor;

void main() {
  ivec4 diffuseColor = texture(u_diffuse, v_texCoord) * u_diffuseMult + u_diffuseOffset;
  if (diffuseColor.a <= 0) {
    discard;
  }
  outColor = vec4(diffuseColor) / 100. * .5 + .5;
  outColor.rbg *= outColor.a;
}
  </script>
  <script id="uint-one-point-fs" type="notjs">
#version 300 es
precision mediump float;

in vec4 v_position;
in vec2 v_texCoord;

uniform uvec4 u_diffuseOffset;
uniform uvec4 u_diffuseMult;
uniform mediump usampler2D u_diffuse;

out vec4 outColor;

void main() {
  uvec4 diffuseColor = texture(u_diffuse, v_texCoord) * u_diffuseMult + u_diffuseOffset;
  if (diffuseColor.a == 0u) {
    discard;
  }
  outColor = vec4(diffuseColor) / 200.;
  outColor.rbg *= outColor.a;
}
  </script>
  <script id="norm-one-point-fs" type="notjs">
#version 300 es
precision mediump float;

in vec4 v_position;
in vec2 v_texCoord;

uniform vec4 u_diffuseMult;
uniform sampler2D u_diffuse;

out vec4 outColor;

void main() {
  vec4 diffuseColor = (texture(u_diffuse, v_texCoord) * .5 + .5) * u_diffuseMult;
  if (diffuseColor.a < 0.1) {
    discard;
  }
  outColor = diffuseColor;
  outColor.rbg *= outColor.a;
}
  </script>
  <script id="float-one-point-fs" type="notjs">
#version 300 es
precision mediump float;

in vec4 v_position;
in vec2 v_texCoord;

uniform vec4 u_diffuseMult;
uniform vec4 u_diffuseOffset;
uniform sampler2D u_diffuse;

out vec4 outColor;

void main() {
  vec4 diffuseColor = texture(u_diffuse, v_texCoord) / 100. * u_diffuseMult + u_diffuseOffset;
  if (diffuseColor.a < 0.1) {
    discard;
  }
  outColor = diffuseColor;
  outColor.rbg *= outColor.a;
}
  </script>
  <script src="../3rdparty/chroma.min.js"></script>
  <script type="module">
import * as twgl from '../dist/4.x/twgl-full.module.js';

// adapted from http://stackoverflow.com/a/6162687/128511
const toHalf = (function() {

  const floatView = new Float32Array(1);
  const int32View = new Int32Array(floatView.buffer);

  return function toHalf(fval) {
    floatView[0] = fval;
    const fbits = int32View[0];
    const sign  = (fbits >> 16) & 0x8000;         // sign only
    let val     = (fbits & 0x7fffffff) + 0x1000;  // rounded value

    if (val >= 0x47800000) {                    // might be or become NaN/Inf
      if ((fbits & 0x7fffffff) >= 0x47800000) {
                                                // is or must become NaN/Inf
        if (val < 0x7f800000) {                 // was value but too large
          return sign | 0x7c00;                 // make it +/-Inf
        }
        return sign | 0x7c00 |                  // remains +/-Inf or NaN
            (fbits & 0x007fffff) >> 13;         // keep NaN (and Inf) bits
      }
      return sign | 0x7bff;                     // unrounded not quite Inf
    }
    if (val >= 0x38800000) {                    // remains normalized value
      return sign | val - 0x38000000 >> 13;     // exp - 127 + 15
    }
    if (val < 0x33000000)  {                    // too small for subnormal
      return sign;                              // becomes +/-0
    }
    val = (fbits & 0x7fffffff) >> 23;            // tmp exp for subnormal calc
    return sign | ((fbits & 0x7fffff | 0x800000) // add subnormal bit
         + (0x800000 >>> val - 102)              // round depending on cut off
         >> 126 - val);                          // div by 2^(1-(exp-127+15)) and >> 13 | exp=0
  };
}());

// Adapted from http://gamedev.stackexchange.com/a/17329/79
const to11uf = (function() {
  const F11_EXPONENT_BITS = 0x1F;
  const F11_EXPONENT_SHIFT = 6;
  const F11_EXPONENT_BIAS = 15;
  const F11_MANTISSA_BITS = 0x3f;
  const F11_MANTISSA_SHIFT = (23 - F11_EXPONENT_SHIFT);
  const F11_MAX_EXPONENT = (F11_EXPONENT_BITS << F11_EXPONENT_SHIFT);

  const floatView = new Float32Array(1);
  const int32View = new Int32Array(floatView.buffer);

  return function(val) {
    floatView[0] = val;
    const f32 = int32View[0];
    let f11 = 0;
    /* Decode IEEE 754 little-endian 32-bit floating-point value */
    if (f32 & 0x80000000) {
      // negative values go to 0
      return 0;
    }
    /* Map exponent to the range [-127,128] */
    let exponent = ((f32 >> 23) & 0xff) - 127;
    let mantissa = f32 & 0x007fffff;
    if (exponent === 128) {
      /* Infinity or NaN */
      f11 = F11_MAX_EXPONENT;
      if (mantissa) {
        f11 |= (mantissa & F11_MANTISSA_BITS);
      }
    } else if (exponent > 15) {
      /* Overflow - flush to Infinity */
      f11 = F11_MAX_EXPONENT;
    } else if (exponent > -15) {
      /* Representable value */
      exponent += F11_EXPONENT_BIAS;
      mantissa >>= F11_MANTISSA_SHIFT;
      f11 = exponent << F11_EXPONENT_SHIFT | mantissa;
    } else {
        f11 = 0;
    }
    return f11;
  };
}());

// Adapted from http://gamedev.stackexchange.com/a/17329/79
const to10uf = (function() {
  const F10_EXPONENT_BITS = 0x1F;
  const F10_EXPONENT_SHIFT = 5;
  const F10_EXPONENT_BIAS = 15;
  const F10_MANTISSA_BITS = 0x1f;
  const F10_MANTISSA_SHIFT = (23 - F10_EXPONENT_SHIFT);
  const F10_MAX_EXPONENT = (F10_EXPONENT_BITS << F10_EXPONENT_SHIFT);

  const floatView = new Float32Array(1);
  const int32View = new Int32Array(floatView.buffer);

  return function(val) {
    floatView[0] = val;
    const f32 = int32View[0];
    let f10 = 0;
    /* Decode IEEE 754 little-endian 32-bit floating-point value */
    if (f32 & 0x80000000) {
      // negative values go to 0
      return 0;
    }
    /* Map exponent to the range [-127,128] */
    let exponent = ((f32 >> 23) & 0xff) - 127;
    let mantissa = f32 & 0x007fffff;
    if (exponent === 128) {
      /* Infinity or NaN */
      f10 = F10_MAX_EXPONENT;
      if (mantissa) {
        f10 |= (mantissa & F10_MANTISSA_BITS);
      }
    } else if (exponent > 15) {
      /* Overflow - flush to Infinity */
      f10 = F10_MAX_EXPONENT;
    } else if (exponent > -15) {
      /* Representable value */
      exponent += F10_EXPONENT_BIAS;
      mantissa >>= F10_MANTISSA_SHIFT;
      f10 = exponent << F10_EXPONENT_SHIFT | mantissa;
    } else {
        f10 = 0;
    }
    return f10;
  };
}());


function main() {
  twgl.setDefaults({attribPrefix: "a_"});
  const m4 = twgl.m4;
  const gl = document.getElementById("c").getContext("webgl2");
  if (!gl) {
    document.querySelector("#no-webgl2").style.display = "";
    return;
  }
  const onePointProgramInfo = twgl.createProgramInfo(gl, ["one-point-vs", "one-point-fs"], ["a_texcoord", "a_position"]);
  const intProgramInfo = twgl.createProgramInfo(gl, ["one-point-vs", "int-one-point-fs"], ["a_texcoord", "a_position"]);
  const uintProgramInfo = twgl.createProgramInfo(gl, ["one-point-vs", "uint-one-point-fs"], ["a_texcoord", "a_position"]);
  const normProgramInfo = twgl.createProgramInfo(gl, ["one-point-vs", "norm-one-point-fs"], ["a_texcoord", "a_position"]);
  const floatProgramInfo = twgl.createProgramInfo(gl, ["one-point-vs", "float-one-point-fs"], ["a_texcoord", "a_position"]);

  const shapes = [
    twgl.primitives.createCubeBufferInfo(gl, 2),
    twgl.primitives.createSphereBufferInfo(gl, 1, 24, 12),
    twgl.primitives.createPlaneBufferInfo(gl, 2, 2),
    twgl.primitives.createTruncatedConeBufferInfo(gl, 1, 0, 2, 24, 1),
  ];

  function rand(min, max) {
    if (max === undefined) {
      max = min;
      min = 0;
    }
    return min + Math.random() * (max - min);
  }

  // Shared values
  const camera = m4.identity();
  const view = m4.identity();
  const viewProjection = m4.identity();

  function rgb565(r, g, b) {
    return (((r * 31) | 0) << 11) |
           (((g * 63) | 0) <<  6) |
           (((b * 31) | 0) <<  0);
  }

  function rgba5551(r, g, b, a) {
    return (((r * 31) | 0) << 11) |
           (((g * 31) | 0) <<  6) |
           (((b * 31) | 0) <<  1) |
           (((a *  1) | 0) <<  0);
  }

  function rgba1010102(r, g, b, a) {
    return (((r * 1023) | 0) <<  0) |
           (((g * 1023) | 0) << 10) |
           (((b * 1023) | 0) << 20) |
           (((a *    3) | 0) << 30);
  }

  function rgb9e5(r, g, b) {
    const e = 15;
    return (((r * 511) | 0) <<  0) |
           (((g * 511) | 0) <<  9) |
           (((b * 511) | 0) << 18) |
           (e               << 27);
  }

  function rgb101111(r, g, b) {
    return (to11uf(r) <<  0) |
           (to11uf(g) << 11) |
           (to10uf(b) << 22);
  }

  const flatten = list => list.reduce(
      (a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []
  );

  const texturesSettings = {
    R8: {   // 	8	 	 	 	 	Y	Y
      internalFormat: gl.R8,
      format: gl.RED,
      type: gl.UNSIGNED_BYTE,
      width: 4,
      minMag: gl.NEAREST,
      src: [ 64, 255, 128, 192 ],
    },
    R8_SNORM: {   // 	s8	 	 	 	 	 	Y
      internalFormat: gl.R8_SNORM,
      format: gl.RED,
      type: gl.BYTE,
      minMag: gl.NEAREST,
      src: [ 127, 64, 32, 96 ],
    },
    R16F: {   // 	f16	 	 	 	 	 	Y
      internalFormat: gl.R16F,
      format: gl.RED,
      type: gl.HALF_FLOAT,
      src: new Uint16Array([toHalf(20), toHalf(40), toHalf(30), toHalf(100)]),
      diffuseOffset: [ 0, 0, 0, 1 ],
    },
    R16Fb: {   // 	f16	 	 	 	 	 	Y
      internalFormat: gl.R16F,
      format: gl.RED,
      type: gl.FLOAT,
      minMag: gl.NEAREST,
      src: [ 20, 40, 30, 100 ],
      diffuseOffset: [ 0, 0, 1, 1 ],
    },
    R32F: {   // 	f32
      internalFormat: gl.R32F,
      format: gl.RED,
      type: gl.FLOAT,
      minMag: gl.NEAREST,
      src: [ 100, 25, 50, 75 ],
      diffuseOffset: [ 0, 1, 0, 1 ],
    },
    R8UI: {   // 	ui8	 	 	 	 	Y
      internalFormat: gl.R8UI,
      format: gl.RED_INTEGER,
      type: gl.UNSIGNED_BYTE,
      src: [ 50, 25, 175, 100 ],
      diffuseOffset: [ 0, 100, 0, 200 ],
    },
    R8I: {    // 	i8	 	 	 	 	Y
      internalFormat: gl.R8I,
      format: gl.RED_INTEGER,
      type: gl.BYTE,
      src: [ 0x100 - 50, 25, 0x100 - 75, 100 ],
      diffuseOffset: [ 0, 0, 0, 100 ],
    },
    R16UI: {    // 	ui16	 	 	 	 	Y
      internalFormat: gl.R16UI,
      format: gl.RED_INTEGER,
      type: gl.UNSIGNED_SHORT,
      src: [ 50, 25, 175, 100 ],
      diffuseOffset: [ 0, 100, 100, 200 ],
    },
    R16I: {   // 	i16	 	 	 	 	Y
      internalFormat: gl.R16I,
      format: gl.RED_INTEGER,
      type: gl.SHORT,
      width: 1,
      src: [ 0x100 - 50, 25, 0x100 - 75, 100 ],
      diffuseOffset: [ 0, 50, 100, 100 ],
    },
    R32UI: {    // 	ui32	 	 	 	 	Y
      internalFormat: gl.R32UI,
      format: gl.RED_INTEGER,
      type: gl.UNSIGNED_INT,
      height: 1,
      src: [ 50, 25, 175, 100 ],
      diffuseOffset: [ 0, 100, 200, 200 ],
    },
    R32I: {   // 	i32	 	 	 	 	Y
      internalFormat: gl.R32I,
      format: gl.RED_INTEGER,
      type: gl.INT,
      src: [ 0x100 - 50, 25, 0x100 - 75, 100 ],
      diffuseOffset: [ 0, 0, 50, 100 ],
    },
    RG8: {    // 	8	8	 	 	 	Y	Y
      internalFormat: gl.RG8,
      format: gl.RG,
      type: gl.UNSIGNED_BYTE,
      minMag: gl.NEAREST,
      src: [ 255, 0, 0, 255 ],
    },
    RG8_SNORM: {    // 	s8	s8	 	 	 	 	Y
      internalFormat: gl.RG8_SNORM,
      format: gl.RG,
      type: gl.BYTE,
      minMag: gl.NEAREST,
      src: [ 32, 16, 64, 127],
    },
    RG16F: {    // 	f16	f16	 	 	 	 	Y
      internalFormat: gl.RG16F,
      format: gl.RG,
      type: gl.HALF_FLOAT,
      minMag: gl.NEAREST,
      src: new Uint16Array([ toHalf(20), toHalf(40), toHalf(60), 0, 0, toHalf(100), 0, 0]),
      diffuseOffset: [0, 0, 0, 1],
    },
    RG16Fb: {    // 	f16	f16	 	 	 	 	Y
      internalFormat: gl.RG16F,
      format: gl.RG,
      type: gl.FLOAT,
      minMag: gl.NEAREST,
      src: [ 100, 100, 100, 50, 50, 100, 50, 70 ],
      diffuseOffset: [0, 0, 0, 1],
    },
    RG32F: {    // 	f32	f32
      internalFormat: gl.RG32F,
      format: gl.RG,
      type: gl.FLOAT,
      minMag: gl.NEAREST,
      src: [ 20, 40, 40, 66, 66, 88, 88, 100 ],
      diffuseOffset: [0, 0, 0, 1],
    },
    RG8UI: {    // 	ui8	ui8	 	 	 	Y
      internalFormat: gl.RG8UI,
      format: gl.RG_INTEGER,
      type: gl.UNSIGNED_BYTE,
      width: 1,
      src: [ 50, 75, 75, 25, 200, 175, 100, 200 ],
      diffuseOffset: [ 0, 0, 200, 200 ],
    },
    RG8I: {   // 	i8	i8	 	 	 	Y
      internalFormat: gl.RG8I,
      format: gl.RG_INTEGER,
      type: gl.BYTE,
      width: 1,
      src: [ 50, 75, 75, 25, 80, 0x100 - 75, 100, 100 ],
      diffuseOffset: [ 0, 0, 80, 100 ],
    },
    RG16UI: {   // 	ui16	ui16	 	 	 	Y
      internalFormat: gl.RG16UI,
      format: gl.RG_INTEGER,
      type: gl.UNSIGNED_SHORT,
      src: [  75, 50, 75, 25, 200, 175, 100, 200 ],
      diffuseOffset: [ 0, 0, 200, 200 ],
    },
    RG16I: {    // 	i16	i16	 	 	 	Y
      internalFormat: gl.RG16I,
      format: gl.RG_INTEGER,
      type: gl.SHORT,
      src: [ 80, 0x100 - 75, 50, 75, 75, 25, 100, 100 ],
      diffuseOffset: [ 0, 0, 60, 100 ],
    },
    RG32UI: {   // 	ui32	ui32	 	 	 	Y
      internalFormat: gl.RG32UI,
      format: gl.RG_INTEGER,
      type: gl.UNSIGNED_INT,
      src: [ 75, 50, 75, 25, 200, 175, 100, 200 ],
      diffuseOffset: [ 0, 0, 100, 200 ],
    },
    RG32I: {    // 	i32	i32	 	 	 	Y
      internalFormat: gl.RG32I,
      format: gl.RG_INTEGER,
      type: gl.INT,
      src: [ 80, 0x100 - 75, 50, 75, 75, 25, 100, 100 ],
      diffuseOffset: [ 0, 0, 20, 100 ],
    },
    RGB8: {   // 	8	8	8	 	 	Y	Y
      internalFormat: gl.RGB8,
      format: gl.RGB,
      type: gl.UNSIGNED_BYTE,
      minMag: gl.NEAREST,
      src: [ 0, 0, 255, 255, 0, 255, 0, 255, 255, 255, 255, 255],
    },
    SRGB8: {    // 	8	8	8	 	 	 	Y
      internalFormat: gl.SRGB8,
      format: gl.RGB,
      type: gl.UNSIGNED_BYTE,
      width: 1,
      minMag: gl.NEAREST,
      src: [
        64, 0, 0,
        0, 96, 0,
        0, 0, 127,
      ],
    },
    RGB565: {   // 	5	6	5	 	 	Y	Y
      internalFormat: gl.RGB565,
      format: gl.RGB,
      type: gl.UNSIGNED_BYTE,
      minMag: gl.NEAREST,
      src: [ 0xFF, 0xFF, 0x55, 0x55, 0xFF, 0xFF ],
    },
    RGB565b: {   // 	5	6	5	 	 	Y	Y
      internalFormat: gl.RGB565,
      format: gl.RGB,
      type: gl.UNSIGNED_SHORT_5_6_5,
      minMag: gl.NEAREST,
      src: new Uint16Array([rgb565(0, 1, 0), rgb565(0, 1, 1)]),
    },
    RGB8_SNORM: {   // 	s8	s8	s8	 	 	 	Y
      internalFormat: gl.RGB8_SNORM,
      format: gl.RGB,
      type: gl.BYTE,
      src: [
        127, 117, 107,
        0x100 - 64, 0x100 - 32, 0x100 - 16,
        12, 32, 64,
      ],
    },
    R11F_G11F_B10F: {   // 	f11	f11	f10	 	 	 	Y
      internalFormat: gl.R11F_G11F_B10F,
      format: gl.RGB,
      type: gl.UNSIGNED_INT_10F_11F_11F_REV,
      src: [
        rgb101111(50, 60, 100),
        rgb101111(0, 0, 0),
        rgb101111(70, 100, 70),
        rgb101111(100, 0, 0),
        rgb101111(0, 100, 0),
        rgb101111(0, 0, 100),
      ],
      diffuseMult: [1, 1, 1, 100],
    },
    R11F_G11F_B10Fb: {   // 	f11	f11	f10	 	 	 	Y
      internalFormat: gl.R11F_G11F_B10F,
      format: gl.RGB,
      type: gl.HALF_FLOAT,
      src: [
        toHalf(50), toHalf( 60), toHalf(100),
        toHalf( 0), toHalf(  0), toHalf(  0),
        toHalf(70), toHalf(100), toHalf(100),
      ],
      diffuseOffset: [0, 0, 0, 1],
    },
    R11F_G11F_B10Fc: {   // 	f11	f11	f10	 	 	 	Y
      internalFormat: gl.R11F_G11F_B10F,
      format: gl.RGB,
      type: gl.FLOAT,
      src: [
        50,  60, 100,
         0,   0,   0,
        70, 100,  70,
        70,  30,  70,
      ],
      diffuseOffset: [0, 0, 0, 1],
    },
    RGB9_E5: {    // 	9	9	9	 	5	 	Y
      internalFormat: gl.RGB9_E5,
      format: gl.RGB,
      type: gl.UNSIGNED_INT_5_9_9_9_REV,
      src: [
        rgb9e5(0.5, 1, 1),
        rgb9e5(  1, 0, 0),
        rgb9e5(0.7, 1, 0.5),
      ],
    },
    RGB9_E5b: {    // 	9	9	9	 	5	 	Y
      internalFormat: gl.RGB9_E5,
      format: gl.RGB,
      type: gl.HALF_FLOAT,
      minMag: gl.NEAREST,
      src: new Uint16Array([
        toHalf(0.2), toHalf(0.1), toHalf(0.1),
        toHalf(0.4), toHalf(0.8), toHalf(0.5),
        toHalf(0.6), toHalf(0.6), toHalf(0.9),
        toHalf(0.8), toHalf(0.4), toHalf(0.5),
        toHalf(0.1), toHalf(0.2), toHalf(0.1),
      ]),
    },
    RGB9_E5c: {    // 	9	9	9	 	5	 	Y
      internalFormat: gl.RGB9_E5,
      format: gl.RGB,
      type: gl.FLOAT,
      width: 2,
      src: [
        0.1, 0.2, 1.1,
        0.2, 0.1, 0.1,
        0.4, 0.5, 0.8,
        0.6, 0.9, 0.6,
        0.8, 0.5, 0.4,
        1.1, 0.1, 0.2,
      ],
    },
    RGB16F: {   // 	f16	f16	f16	 	 	 	Y
      internalFormat: gl.RGB16F,
      format: gl.RGB,
      type: gl.HALF_FLOAT,
      minMag: gl.NEAREST,
      src: [ toHalf(1), toHalf(40), toHalf(90), toHalf(20), toHalf(70), toHalf(50) ],
      diffuseOffset: [ 0, 0, 0, 1 ],
    },
    RGB16Fb: {   // 	f16	f16	f16	 	 	 	Y
      internalFormat: gl.RGB16F,
      format: gl.RGB,
      type: gl.FLOAT,
      src: [ 90, 100, 80, 70, 50, 50 ],
      diffuseOffset: [ 0, 0, 0, 1 ],
    },
    RGB32F: {   // 	f32	f32	f32
      internalFormat: gl.RGB32F,
      format: gl.RGB,
      type: gl.FLOAT,
      src: [
         50,  100, 50,
         50,   90, 50,
         50,   80, 50,
         50,   70, 50,
         50,   60, 50,
         50,   40, 50,
         50,   30, 50,
         50,   20, 50,
      ],
      diffuseOffset: [ 0, 0, 0, 1 ],
    },
    RGB8UI: {   // 	ui8	ui8	ui8
      internalFormat: gl.RGB8UI,
      format: gl.RGB_INTEGER,
      type: gl.UNSIGNED_BYTE,
      src: [
        200, 150, 100,
        180, 175,  90,
        160, 187,  80,
      ],
      diffuseOffset: [ 0, 0, 0, 200 ],
    },
    RGB8I: {    // 	i8	i8	i8
      internalFormat: gl.RGB8I,
      format: gl.RGB_INTEGER,
      type: gl.BYTE,
      src: [
        0x100 - 50, 25,  100,
        0x100 - 75, 100, 20,
        0x100 - 50, 25,  100,
        0x100 - 75, 60, 40,
        0x100 - 50, 25,  100,
        0x100 - 75, 20, 60,
      ],
      diffuseOffset: [ 0, 0, 0, 100 ],
    },
    RGB16UI: {    // 	ui16	ui16	ui16
      internalFormat: gl.RGB16UI,
      format: gl.RGB_INTEGER,
      type: gl.UNSIGNED_SHORT,
      src: [
        150, 100, 200,
        175,  90, 180,
        187,  80, 160,
      ],
      diffuseOffset: [ 0, 0, 0, 200 ],
    },

    RGB16I: {   // 	i16	i16	i16
      internalFormat: gl.RGB16I,
      format: gl.RGB_INTEGER,
      type: gl.SHORT,
      src: [
         25, 100, 0x100 - 50,
        100,  20, 0x100 - 75,
         25, 100, 0x100 - 50,
         60,  40, 0x100 - 75,
         25, 100, 0x100 - 50,
         20,  60, 0x100 - 75,
      ],
      diffuseOffset: [ 0, 0, 0, 100 ],
    },
    RGB32UI: {    // 	ui32	ui32	ui32
      internalFormat: gl.RGB32UI,
      format: gl.RGB_INTEGER,
      type: gl.UNSIGNED_INT,
      src: [
        150, 100, 200,
        175,  90, 180,
        187,  80, 160,
      ],
      diffuseOffset: [ 0, 0, 0, 200 ],
    },
    RGB32I: {   // 	i32	i32	i32
      internalFormat: gl.RGB32I,
      format: gl.RGB_INTEGER,
      type: gl.INT,
      src: [
        0x100 - 50,  100,  25,
        0x100 - 75,   20, 100,
        0x100 - 50,  100,  25,
        0x100 - 75,   40,  60,
        0x100 - 50,  100,  25,
        0x100 - 75,   60,  20,
      ],
      diffuseOffset: [ 0, 0, 0, 100 ],
    },
    RGBA8: {    // 	8	8	8	8	 	Y	Y
      internalFormat: gl.RGBA8,
      format: gl.RGBA,
      type: gl.UNSIGNED_BYTE,
      width: 1,
      src: [
        0x80, 0x90, 0x80, 0x80,
        0x80, 0xA0, 0x80, 0xA0,
        0x80, 0xB0, 0x80, 0xC0,
        0x80, 0xC0, 0x80, 0xFF,
      ],
    },
    SRGB8_ALPHA8: {   // 	8	8	8	8	 	Y	Y
      internalFormat: gl.SRGB8_ALPHA8,
      format: gl.RGBA,
      type: gl.UNSIGNED_BYTE,
      src: [
        0x80, 0x80, 0x90, 0x80,
        0x80, 0x80, 0xA0, 0xA0,
        0x80, 0x80, 0xB0, 0xC0,
        0x80, 0x80, 0xC0, 0xFF,
      ],
    },
    RGBA8_SNORM: {    // 	s8	s8	s8	s8	 	 	Y
      internalFormat: gl.RGBA8_SNORM,
      format: gl.RGBA,
      type: gl.BYTE,
      src: [
        0x7F, 0x60, 0x40, 0x60,
        0x7F, 0x60, 0x40, 0x65,
        0x7F, 0x60, 0x40, 0x70,
        0x7F, 0x60, 0x40, 0x7F,
      ],
    },
    RGB5_A1: {    // 	5	5	5	1	 	Y	Y
      internalFormat: gl.RGB5_A1,
      format: gl.RGBA,
      type: gl.UNSIGNED_BYTE,
      minMag: gl.NEAREST,
      src: [
        0xFF, 0x80, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0x00, 0xFF, 0x80, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0x00,
        0x80, 0xFF, 0x80, 0x00, 0x80, 0x80, 0xFF, 0xFF, 0x80, 0xFF, 0x80, 0x00, 0x80, 0x80, 0xFF, 0xFF,
        0xFF, 0x80, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0x00, 0xFF, 0x80, 0x80, 0xFF, 0x80, 0xFF, 0x80, 0x00,
        0x80, 0xFF, 0x80, 0x00, 0x80, 0x80, 0xFF, 0xFF, 0x80, 0xFF, 0x80, 0x00, 0x80, 0x80, 0xFF, 0xFF,
      ],
    },
    RGB5_A1b: {    // 	5	5	5	1	 	Y	Y
      internalFormat: gl.RGB5_A1,
      format: gl.RGBA,
      type: gl.UNSIGNED_SHORT_5_5_5_1,
      minMag: gl.NEAREST,
      wrap: gl.CLAMP_TO_EDGE,
      src: [
        rgba5551(0.75, 0.75, 1, 1),
        rgba5551(0.75, 0.75, 1, 1),
        rgba5551(0.75, 0.75, 1, 0),
        rgba5551(0.75, 0.75, .8, 1),
      ],
    },
    RGB5_A1c: {    // 	5	5	5	1	 	Y	Y
      internalFormat: gl.RGB5_A1,
      format: gl.RGBA,
      type: gl.UNSIGNED_INT_2_10_10_10_REV,
      minMag: gl.NEAREST,
      wrap: gl.CLAMP_TO_EDGE,
      width: 1,
      src: [
        rgba1010102(0, .1, 1, 1),
        rgba1010102(0, .2, 1, 1),
        rgba1010102(0, .4, 1, 1),
      ],
    },
    RGBA4: {    // 	4	4	4	4	 	Y	Y
      internalFormat: gl.RGBA4,
      format: gl.RGBA,
      type: gl.UNSIGNED_BYTE,
      src: [
        0xFF, 200, 100, 0xFF,
        100, 0xFF, 100, 0xFF,
        200, 100, 0xFF, 0xFF,
        0xFF, 0xCF, 0xFF, 0xFF,
      ],
    },
    RGBA4b: {    // 	4	4	4	4	 	Y	Y
      internalFormat: gl.RGBA4,
      format: gl.RGBA,
      type: gl.UNSIGNED_SHORT_4_4_4_4,
      minMag: gl.NEAREST,
      src: new Uint16Array([
          0xF00F,
          0xF008,
          0xFF0F,
          0xFF08,
          0x0F0F,
          0x0F08,
          0x0FFF,
          0x0FF8,
          0x00FF,
          0x00F8,
      ]),
    },
//    RGB10_A2: {   // 	10	10	10	2	 	Y	Y
//      internalFormat: gl.RGB10_A2,
//      format: gl.RGBA,
//      type: gl.UNSIGNED_INT_2_10_10_10_REV,
//      src: ,
//    },
    RGBA16F: {    // 	f16	f16	f16	f16	 	 	Y
      internalFormat: gl.RGBA16F,
      format: gl.RGBA,
      type: gl.HALF_FLOAT,
      src: new Uint16Array([
        toHalf( 80), toHalf(100), toHalf( 50), toHalf(100),
        toHalf( 70), toHalf( 50), toHalf(100), toHalf( 80),
        toHalf( 50), toHalf(100), toHalf( 50), toHalf( 60),
        toHalf( 40), toHalf( 50), toHalf(100), toHalf( 40),
        toHalf( 30), toHalf(100), toHalf( 50), toHalf( 20),
      ]),
    },
    RGBA16Fb: {    // 	f16	f16	f16	f16	 	 	Y
      internalFormat: gl.RGBA16F,
      format: gl.RGBA,
      type: gl.FLOAT,
      src: (function(w, h) {
        const m = [];
        for (let y = 0; y < w; ++y) {
          for (let x = 0; x < h; ++x) {
            m.push(chroma.hsv(Math.abs(Math.atan2(x / (w - 1) - 0.5, y / (h - 1) - 0.5)) / Math.PI * 180, .8, 1).gl().map(x => x * 100));
          }
        }
        return flatten(m);
      }(16, 16)),
    },
    RGBA32F: {    // 	f32	f32	f32	f32
      internalFormat: gl.RGBA32F,
      format: gl.RGBA,
      type: gl.FLOAT,
      src: [
        100, 90, 80, 100,
        100, 90, 80,  50,
         90, 80, 70, 100,
         90, 80, 70,  50,
         80, 70, 60, 100,
         80, 70, 60,  50,
         70, 60, 50, 100,
      ],
    },
    RGBA8UI: {    // 	ui8	ui8	ui8	ui8	 	Y
      internalFormat: gl.RGBA8UI,
      format: gl.RGBA_INTEGER,
      type: gl.UNSIGNED_BYTE,
      width: 5,
      src: [
        180, 160, 200, 200,
        180, 150, 200, 100,
        180, 140, 200, 200,
        180, 130, 200, 100,
        180, 120, 200, 200,
        180, 160, 200, 100,
        180, 150, 200, 200,
        180, 140, 200, 100,
        180, 130, 200, 200,
        180, 120, 200, 100,
      ],
    },
    RGBA8I: {   // 	i8	i8	i8	i8	 	Y
      internalFormat: gl.RGBA8I,
      format: gl.RGBA_INTEGER,
      type: gl.BYTE,
      src: [
        80, 100, 60,  20,
        80, 100, 50, 100,
        80, 100, 40,  20,
        80, 100, 30, 100,
        80, 100, 20,  20,
        80, 100, 60, 100,
        80, 100, 50,  20,
        80, 100, 40, 100,
        80, 100, 30,  20,
        80, 100, 20, 100,
      ],
    },
    RGB10_A2UI: {   // 	ui10	ui10	ui10	ui2	 	Y
      internalFormat: gl.RGB10_A2UI,
      format: gl.RGBA_INTEGER,
      type: gl.UNSIGNED_INT_2_10_10_10_REV,
      src: new Uint32Array([
        rgba1010102(0.09, 0.06, 1, 1),
        rgba1010102(0.08, 0.06, 1, 0.75),
        rgba1010102(0.07, 0.06, 1, 0.5),
        rgba1010102(0.06, 0.06, 1, 0.3),
      ]),
      diffuseMult: [1, 1, 1, 50],
      diffuseOffset: [0, 0, 0, 0],
    },
    RGBA16UI: {   // 	ui16	ui16	ui16	ui16	 	Y
      internalFormat: gl.RGBA16UI,
      format: gl.RGBA_INTEGER,
      type: gl.UNSIGNED_SHORT,
      src: [
        120, 160, 160, 160,
        140,  80, 140, 140,
        120, 120,  60, 120,
      ],
    },
    RGBA16I: {    // 	i16	i16	i16	i16	 	Y
      internalFormat: gl.RGBA16I,
      format: gl.RGBA_INTEGER,
      type: gl.SHORT,
      src: [
        20, 60, 60, 100,
        40, 80, 40,  80,
        20, 20, 60,  60,
      ],
    },
    RGBA32I: {    // 	i32	i32	i32	i32	 	Y
      internalFormat: gl.RGBA32I,
      format: gl.RGBA_INTEGER,
      type: gl.INT,
      src: [
        100, 90, 80, 80,
         90, 90, 80, 80,
         80, 90, 80, 80,
         70, 90, 80, 80,
      ],
    },
    RGBA32UI: {   // 	ui32	ui32	ui32	ui32	 	Y
      internalFormat: gl.RGBA32UI,
      format: gl.RGBA_INTEGER,
      type: gl.UNSIGNED_INT,
      src: [
        200, 90, 80, 180,
        190, 90, 80, 180,
        180, 90, 80, 180,
        170, 90, 80, 180,
      ],
    },
    // Sized Internal Format	Format	Type	Depth Bits	Stencil Bits
    DEPTH_COMPONENT16: {    // 	16
      internalFormat: gl.DEPTH_COMPONENT16,
      format: gl.DEPTH_COMPONENT,
      type: gl.UNSIGNED_SHORT,
      src: [
        0xFFFF, 0x7FFF, 0xCFFF,
      ],
    },
    DEPTH_COMPONENT16b: {    // 	16
      internalFormat: gl.DEPTH_COMPONENT16,
      format: gl.DEPTH_COMPONENT,
      type: gl.UNSIGNED_INT,
      src: [
        0xFFFFFFFF, 0xCFFFFFFF, 0x7FFFFFFF,
      ],
    },
    DEPTH_COMPONENT24: {    // 	24
      internalFormat: gl.DEPTH_COMPONENT24,
      format: gl.DEPTH_COMPONENT,
      type: gl.UNSIGNED_INT,
      src: [
         0xFFFFFFFF, 0x7FFFFFFF, 0x3FFFFFFF,
      ],
      diffuseOffset: [ 0, 0, 0, 1, ],
    },
    DEPTH_COMPONENT32F: {   // 	f32
      internalFormat: gl.DEPTH_COMPONENT32F,
      format: gl.DEPTH_COMPONENT,
      type: gl.FLOAT,
      src: [ 1, 0.7, 0.5, 0.3 ],
      diffuseMult: [ 100, 100, 100, 1 ],
      diffuseOffset: [ 0, 0, 0, 1 ],
    },
    DEPTH24_STENCIL8: {   // 	24	8
      internalFormat: gl.DEPTH24_STENCIL8,
      format: gl.DEPTH_STENCIL,
      type: gl.UNSIGNED_INT_24_8,
      src: [
         0xFFFFFFFF, 0x7FFFFFFF, 0x3FFFFFFF,
         0x5FFFFFFF, 0xAFFFFFFF, 0xCFFFFFFF,
      ],
    },
    //DEPTH32F_STENCIL8: {    // 	f32	8
    //  internalFormat: gl.DEPTH32F_STENCIL8,
    //  format: gl.DEPTH_STENCIL,
    //  type: gl.FLOAT_32_UNSIGNED_INT_24_8_REV,
    //  src: ,
    //},
  };
  const textures = twgl.createTextures(gl, texturesSettings);

  // This is soley to make it easy to pick textures at random
  const twoDTextureNames = Object.keys(textures);

  const objects = [];
  const drawObjects = [];
  const numObjects = 200;

//  total / across / aspect = down
//  200


  for (let ii = 0; ii < numObjects; ++ii) {
    const shape = shapes[ii % shapes.length];
    const texName = twoDTextureNames[ii % twoDTextureNames.length];
    const texture = textures[texName];
    const texSettings = texturesSettings[texName];
    let programInfo = onePointProgramInfo;
    if (texName.endsWith("UI")) {
      programInfo = uintProgramInfo;
    } else if (texName.endsWith("I")) {
      programInfo = intProgramInfo;
    } else if (texName.indexOf("F") >= 0 ) {
      programInfo = floatProgramInfo;
    } else if (texName.indexOf("SNORM") >= 0) {
      programInfo = normProgramInfo;
    }
    const uniforms = {
      u_diffuseMult: texSettings.diffuseMult || [1, 1, 1, 1], //chroma.hsv((baseHue + rand(0, 60)) % 360, 0.4, 0.8).gl(),
      u_diffuseOffset: texSettings.diffuseOffset || [0, 0, 0, 0],
      u_diffuse: texture,
      u_viewInverse: camera,
      u_world: m4.identity(),
      u_worldInverseTranspose: m4.identity(),
      u_worldViewProjection: m4.identity(),
    };
    drawObjects.push({
      programInfo: programInfo,
      bufferInfo: shape,
      uniforms: uniforms,
    });
    objects.push({
      translation: [rand(-10, 10), rand(-10, 10), rand(-10, 10)],
      ySpeed: rand(0.1, 0.3),
      zSpeed: rand(0.1, 0.3),
      uniforms: uniforms,
    });
  }

  function render(time) {
    time *= 0.001;
    twgl.resizeCanvasToDisplaySize(gl.canvas);
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

    gl.enable(gl.DEPTH_TEST);
    gl.enable(gl.BLEND);
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    const radius = 20;
    const orbitSpeed = time * 0.1;
    const projection = m4.perspective(30 * Math.PI / 180, gl.canvas.clientWidth / gl.canvas.clientHeight, 0.5, 100);
    const eye = [Math.cos(orbitSpeed) * radius, 4, Math.sin(orbitSpeed) * radius];
    const target = [0, 0, 0];
    const up = [0, 1, 0];

    m4.lookAt(eye, target, up, camera);
    m4.inverse(camera, view);
    m4.multiply(projection, view, viewProjection);

    objects.forEach(function(obj) {
      const uni = obj.uniforms;
      const world = uni.u_world;
      m4.identity(world);
      m4.rotateY(world, time * obj.ySpeed, world);
      m4.rotateZ(world, time * obj.zSpeed, world);
      m4.translate(world, obj.translation, world);
      m4.rotateX(world, time, world);
      m4.transpose(m4.inverse(world, uni.u_worldInverseTranspose), uni.u_worldInverseTranspose);
      m4.multiply(viewProjection, uni.u_world, uni.u_worldViewProjection);
    });

    twgl.drawObjectList(gl, drawObjects);

    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
}
main();
  </script>
</html>




